package com.dosgi.module;

import java.io.IOException;
import java.net.URLDecoder;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dosgi.Dosgi;
import com.dosgi.classloader.DosgiClassLoader;
import com.dosgi.clazz.ClassHandler;
import com.dosgi.kit.ManiFestKit;
import com.dosgi.kit.StrKit;

public class ModuleContext implements IModuleContext {
	private static transient final Logger LOG = LoggerFactory.getLogger(ModuleContext.class);
	private Map<String, String> manifestAttributes;

	private String filePath;

	private ClassLoader classLoader;

	private String symbolicName;

	private String version;

	private IModule bootInstance;

	private Set<String> exportPackages = Collections.emptySet();;

	private Map<String, String> importPackages = Collections.emptyMap();

	private Map<String, String> requireModules = Collections.emptyMap();

	private Collection<IModuleContext> resolvedRequireModules = null;

	private Collection<IModuleContext> referenceModules = null;

	private String phase;

	private String classPrefix;

	private boolean running = false;

	public ModuleContext(String moduleFile) throws Exception {
		this.manifestAttributes = ManiFestKit.getMainAttributes(new JarFile(moduleFile));
		this.filePath = moduleFile;
		this.symbolicName = manifestAttributes.get("Implementation-Title");
		this.version = manifestAttributes.get("Implementation-Version");
		this.phase = manifestAttributes.get("Phase");
		this.classPrefix = manifestAttributes.get("Module-Class-Prefix");
		initExportPackages();
		initImportPackages();
		initRequireModules();
	}

	private void initRequireModules() {
		String requireModuleStr = manifestAttributes.get("Require-Module");
		if (StrKit.isNotBlank(requireModuleStr)) {
			HashMap<String, String> _importModules = new HashMap<String, String>();
			String[] split = StrKit.split(requireModuleStr, ",");
			for (String str : split) {
				String[] nameVersion = getNameVersion(str);
				_importModules.put(nameVersion[0], nameVersion[1]);
			}
			requireModules = Collections.unmodifiableMap(_importModules);
		}
	}

	private String[] getNameVersion(String str) {
		String[] nameVersion = new String[2];
		if (str.indexOf(';') >= 0) {
			String[] split2 = StrKit.split(str, ";");
			nameVersion[0] = split2[0].trim();
			if (split2.length == 2)
				nameVersion[1] = split2[1].trim();
		} else
			nameVersion[0] = str.trim();
		return nameVersion;
	}

	private void initImportPackages() {
		String importPkg = manifestAttributes.get("Import-Package");
		if (StrKit.isNotBlank(importPkg)) {
			HashMap<String, String> _importPackages = new HashMap<String, String>();
			String[] split = StrKit.split(importPkg, ",");
			for (String str : split) {
				String[] nameVersion = getNameVersion(str);
				_importPackages.put(nameVersion[0], nameVersion[1]);
			}
			importPackages = Collections.unmodifiableMap(_importPackages);
		}
	}

	private void initExportPackages() {
		String exportPkg = manifestAttributes.get("Export-Package");
		if (StrKit.isNotBlank(exportPkg)) {
			HashSet<String> _exportPackages = new HashSet<String>();
			String[] split = StrKit.split(exportPkg, ",");
			for (String str : split) {
				_exportPackages.add(str.trim());
			}
			exportPackages = Collections.unmodifiableSet(_exportPackages);
		}
	}

	public ClassLoader getClassLoader() {
		return classLoader;
	}

	@Override
	public void init() throws Exception {
		classLoader = new DosgiClassLoader(this, ModuleContext.class.getClassLoader());
		getInstance().init(this);
	}

	public void start() throws Exception {
		if (running)
			return;
		getInstance().start(this);
		running = true;
	}

	@Override
	public void stop() throws Exception {
		if (!running)
			return;
		getInstance().stop(this);
		bootInstance = null;
		running = false;
	}

	public Map<String, String> getManifestAttributes() {
		return Collections.unmodifiableMap(manifestAttributes);
	}

	public String getSymbolicName() {
		return this.symbolicName;
	}

	public String getVersion() {
		return this.version;
	}

	public String getQualifiedName() {
		return String.format("%s-%s", this.symbolicName, this.version);
	}

	@Override
	public void registry(Class<?> clazz, Object bean) {
		Dosgi.context().beanFactory().registry(clazz, bean);
	}

	@Override
	public <T> T get(Class<T> clazz) {
		return Dosgi.context().beanFactory().get(clazz);
	}

	private IModule getInstance() throws Exception {
		if (bootInstance == null)
			synchronized (this) {
				if (bootInstance == null) {
					Class<?> mClass = getClassLoader().loadClass(manifestAttributes.get(BOOT_CLASS));
					if (IModule.class.isAssignableFrom(mClass))
						bootInstance = (IModule) mClass.newInstance();
					else
						throw new Exception(String.format("Module Boot-Class(%s) must implements IModule",
								mClass.getName()));
				}

			}
		return bootInstance;
	}

	public String getFilePath() {
		return filePath;
	}

	public Set<String> getExportPackages() {
		return exportPackages;
	}

	public Map<String, String> getImportPackages() {
		return importPackages;
	}

	public Map<String, String> getRequireModules() {
		return requireModules;
	}

	/**
	 * @return the 获取RequireModules声明的模块
	 */
	public final Collection<IModuleContext> getResolvedRequireModules() {
		if (resolvedRequireModules != null)
			return resolvedRequireModules;
		HashSet<IModuleContext> set = new HashSet<IModuleContext>();
		// 解析声明的依赖模块
		for (Entry<String, String> entry : requireModules.entrySet()) {
			String symbolicName = entry.getKey();
			String version = entry.getValue();
			IModuleContext mContext = Dosgi.context().getModule(symbolicName, version);
			if (mContext == null)
				continue;
			set.add(mContext);
			mContext.addReferenceModule(this);
		}
		resolvedRequireModules = Collections.unmodifiableSet(set);
		return resolvedRequireModules;
	}

	/**
	 * @return the 获取RequireModules声明和ImportPackages声明的模块
	 */
	public final Collection<IModuleContext> getResolvedRequireModulesWithImport() {
		Collection<IModuleContext> resolvedRequireModulesWithImport = getResolvedRequireModules();
		if (importPackages.isEmpty())
			return resolvedRequireModulesWithImport;
		resolvedRequireModulesWithImport = new HashSet<IModuleContext>(resolvedRequireModulesWithImport);
		// 解析导入包依赖的模块
		for (Entry<String, String> entry : importPackages.entrySet()) {
			String packageName = entry.getKey();
			String version = entry.getValue();
			IModuleContext mContext = Dosgi.context().getModuleByExportPackage(packageName, version);
			if (mContext == null)
				continue;
			resolvedRequireModulesWithImport.add(mContext);
			mContext.addReferenceModule(this);
		}
		return resolvedRequireModulesWithImport;
	}

	@Override
	public String getPhase() {
		return phase;
	}

	@Override
	public String getClassPrefix() {
		return classPrefix;
	}

	/**
	 * @return the running
	 */
	public final boolean isRunning() {
		return running;
	}

	@Override
	public void scanClasses() throws Exception {
		String classPrefix = StrKit.blankDefault(getClassPrefix(), "");
		JarFile jarFile = null;
		try {
			Class<?> clazz = null;
			filePath = URLDecoder.decode(filePath, "UTF-8");
			jarFile = new JarFile(filePath);
			Enumeration<JarEntry> entries = jarFile.entries();
			while (entries.hasMoreElements()) {
				JarEntry jarEntry = entries.nextElement();
				String entryName = jarEntry.getName();
				// 限定一下包路径，否则有性能问题，或者根据模块的配置文件来读指定包路径，先临时解决了
				if (!jarEntry.isDirectory() && entryName.startsWith(classPrefix) && entryName.endsWith(".class")) {
					String className = entryName.replace("/", ".").substring(0, entryName.length() - 6);
					try {
						clazz = Class.forName(className, false, classLoader);
					} catch (Throwable ex) {
						// ignore
					}
					if (clazz != null) {
						// 处理class逻辑
						for (ClassHandler classHandler : Dosgi.context().getClassHandlers()) {
							try {
								classHandler.handle(clazz);
							} catch (Exception e) {
								LOG.error("handle class failed. class:" + className + ",handler:"
										+ classHandler.getClass().getName(), e);
							}
						}
					}
				}
			}
		} finally {
			if (jarFile != null)
				try {
					jarFile.close();
				} catch (IOException e) {
				}
		}

	}

	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((symbolicName == null) ? 0 : symbolicName.hashCode());
		result = prime * result + ((version == null) ? 0 : version.hashCode());
		return result;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ModuleContext other = (ModuleContext) obj;
		if (symbolicName == null) {
			if (other.symbolicName != null)
				return false;
		} else if (!symbolicName.equals(other.symbolicName))
			return false;
		if (version == null) {
			if (other.version != null)
				return false;
		} else if (!version.equals(other.version))
			return false;
		return true;
	}

	@Override
	public final void addReferenceModule(IModuleContext mContext) {
		if (referenceModules == null)
			referenceModules = new HashSet<IModuleContext>();
		referenceModules.add(mContext);
	}

	/**
	 * @return the referenceModules
	 */
	@Override
	public final Collection<IModuleContext> getReferenceModules() {
		return referenceModules;
	}
}
